#include "RendererDX9Main.h"

D3DVERTEXELEMENT9 VertexPosElements[] = 
{
        {0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		{0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
        D3DDECL_END()
}; 

bool Renderer9::Initialize(HWND hwnd)
{
	m_pd3d = Direct3DCreate9(D3D_SDK_VERSION);
	if(m_pd3d == NULL)
	{
		return false;
	}
	D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof( d3dpp ) );
    d3dpp.Windowed = true;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.EnableAutoDepthStencil = true;
	if	(FAILED	(	m_pd3d->CreateDevice	(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, 
					&d3dpp, &m_pd3d9Device	) // device params
				) // failed param
		) // if failed
	{
		return false;
	}
	return true;

}

void Renderer9::VBeginScene()
{
	m_pd3d9Device->BeginScene();
}
void Renderer9::VEndScene()
{
	m_pd3d9Device->EndScene();
}

void Renderer9::VShowCursor(const bool show_cursor)
{
	m_pd3d9Device->ShowCursor(show_cursor);
}

void Renderer9::VValidateDevice(unsigned long num_passes)
{
	m_pd3d9Device->ValidateDevice(&num_passes);
}

void Renderer9::VResetDeviceState(U32 width, U32 height)
{
	D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof( d3dpp ) );
    d3dpp.Windowed = true;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.BackBufferHeight = height;
	d3dpp.BackBufferWidth = width;
	m_pd3d9Device->Reset(&d3dpp);
	m_pd3d9Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
	m_pd3d9Device->SetRenderState(D3DRS_ZENABLE,  D3DZB_TRUE);
	VSetDeclaration(VertexPosElements);
}
bool Renderer9::VPresentBackBuffer()
{
	if(m_pd3d9Device->Present(0,0,0,0) == D3DERR_DEVICELOST)
	{
		return false;
	}
	return true;
}
RE_DEVSTATE Renderer9::VRequestState()
{
	switch(m_pd3d9Device->TestCooperativeLevel())
	{
		case D3D_OK:
		{
			return RE_OK;
		}
		case D3DERR_DEVICELOST:
		{
			return RE_LOST;
		}
		case D3DERR_DEVICENOTRESET:
		{
			return RE_RESET;
		}
		default:
		{
			return RE_ERROR;
		}
	}
}
void Renderer9::VDrawMesh(RE_Mesh * mesh)
{
	//check if the effect is available
	if(m_pREffect == NULL)
	{
		return;
	}
	if(m_pREffect->pd3dEffect->ValidateTechnique("SimpleRender") != S_OK)
	{
		return;
	}
	HRESULT hr;
	D3DXMATRIXA16 WorldView = m_World * m_View;
	VSetTexture(dynamic_cast<RE_Texture*>(m_pMaterial->m_Texture.get()));
	unsigned int passes;
	hr = m_pREffect->pd3dEffect->SetTechnique("SimpleRender");
	hr = m_pREffect->pd3dEffect->SetMatrix("c_WorldView", &WorldView);
	m_pREffect->pd3dEffect->SetMatrix("c_Proj", &m_Proj);

	D3DXVECTOR4 DiffuseDirV(1,-1, 1, 1);
	D3DXVec4Transform(&DiffuseDirV, &DiffuseDirV, &WorldView);
	m_pREffect->pd3dEffect->SetVector("c_DiffuseDir", &DiffuseDirV); // this will be removed when scene graph is ready
	m_pREffect->pd3dEffect->SetVector("c_DiffuseColor", &D3DXVECTOR4(1,1,1,1));
	m_pREffect->pd3dEffect->SetVector("c_SpecularColor", &D3DXVECTOR4(1,1,1,1));
	m_pREffect->pd3dEffect->SetVector("c_AmbientColor", &D3DXVECTOR4(1,1,1,1));
	if(m_pMaterial != NULL)
	{
		m_pREffect->pd3dEffect->SetVector("c_AmbientRefl", &m_pMaterial->m_Ambient);
		m_pREffect->pd3dEffect->SetVector("c_DiffuseRefl", &m_pMaterial->m_Diffuse);
		m_pREffect->pd3dEffect->SetVector("c_SpecularRefl", &m_pMaterial->m_Specular);
		m_pREffect->pd3dEffect->SetFloat("c_SpecularPower", m_pMaterial->m_SpecularPower);
	}else
	{
		m_pREffect->pd3dEffect->SetVector("c_AmbientRefl", &D3DXVECTOR4(1,1,1,1));
		m_pREffect->pd3dEffect->SetVector("c_DiffuseRefl", &D3DXVECTOR4(1,1,1,1));
		m_pREffect->pd3dEffect->SetVector("c_SpecularRelf", &D3DXVECTOR4(1,1,1,1));
		m_pREffect->pd3dEffect->SetFloat("c_SpecularPower", 1);
	}
	if(m_pTexture != NULL)
	{
		m_pREffect->pd3dEffect->SetTexture("c_DiffuseMap", m_pTexture->pd3d9Texture);
	}
	//hr = DXUTGetD3D9Device()->SetIndices(mesh->m_IB9);
	hr = m_pd3d9Device->SetStreamSource(0, mesh->m_VB9, 0, sizeof(VTX_POSITION_NORMAL_TEX));
	hr = m_pREffect->pd3dEffect->Begin(&passes, 0);
	for(unsigned int iPass = 0; iPass < passes; iPass++)
	{
		hr = m_pREffect->pd3dEffect->BeginPass(iPass);

		//hr = ::DXUTGetD3D9Device()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh->m_Vertices.size(), 0, mesh->m_Indeces.size() / 3);
		m_pd3d9Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, mesh->m_Vertices.size() / 3);

		hr = m_pREffect->pd3dEffect->EndPass();
	}
	hr = m_pREffect->pd3dEffect->End();
}
void Renderer9::VUpdateIndexBuffer(RE_Mesh * mesh) // not used. 
{ /*
	HRESULT hr;
	if(mesh->m_IB9 == NULL)
	{
		hr = DXUTGetD3D9Device()->CreateIndexBuffer(mesh->m_Indeces.size() * sizeof(unsigned int), D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &mesh->m_IB9, 0);
	}
	unsigned int * indeces = NULL;
	mesh->m_IB9->Lock(0, mesh->m_Indeces.size(), (void **)&indeces, 0);
	std::copy( mesh->m_Indeces.begin(), mesh->m_Indeces.end(), indeces);
	mesh->m_IB9->Unlock(); */
}
void Renderer9::VUpdateVertexBuffer(RE_Mesh * mesh)
{
	HRESULT hr;
	if(mesh->m_VB9 == NULL)
	{
		hr = m_pd3d9Device->CreateVertexBuffer(mesh->m_Vertices.size() * sizeof(VTX_POSITION_NORMAL_TEX), D3DUSAGE_WRITEONLY, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX0, D3DPOOL_DEFAULT, &mesh->m_VB9, 0);
	}
	VTX_POSITION_NORMAL_TEX * vertices = NULL;
	mesh->m_VB9->Lock(0, mesh->m_Vertices.size() * sizeof(VTX_POSITION_NORMAL_TEX), (void **)&vertices, 0);
	std::copy( mesh->m_Vertices.begin(), mesh->m_Vertices.end(), vertices);
	mesh->m_VB9->Unlock();
}
bool Renderer9::VReleaseBuffers(RE_Mesh * mesh)
{
	if(mesh->m_VB9 != NULL)
	{
		mesh->m_VB9->Release();
		mesh->m_VB9 = NULL;
	}
	/*
	if(mesh->m_IB9 != NULL)
	{
		mesh->m_IB9->Release();
		mesh->m_IB9 = NULL;
	} */
	return true;
}
void Renderer9::VSetWorldMatrix(const Mat4x4 * const world)
{
	m_World = *world;
}
const Mat4x4* Renderer9::VGetWorldMatrix()
{
	return (const Mat4x4*)&m_World;
}
void Renderer9::VSetViewMatrix(const Mat4x4 * const view)
{
	m_View = *view;
}
const Mat4x4* Renderer9::VGetViewMatrix()
{
	return (const Mat4x4*)&m_View;
}
void Renderer9::VSetProjMatrix(const Mat4x4 * const proj)
{
	m_Proj = *proj;
}
const Mat4x4* Renderer9::VGetProjMatrix()
{
	return (const Mat4x4*)&m_Proj;
}
void Renderer9::VCreateEffect(RE_Effect * effect)
{
	ID3DXBuffer* pd3dErrors = NULL;

	ResHandlePtr handle = REC()->VGetHandle(effect->m_Source);
	D3DXCreateEffect(m_pd3d9Device, handle->VGetBuffer(), handle->VGetSize(), 0, 0, 0, NULL, &effect->pd3dEffect, &pd3dErrors);

	if(pd3dErrors)
	{
		char * errormsg = (char*)pd3dErrors->GetBufferPointer();
		pd3dErrors->Release();
		exit(-1);
	}
	if(!effect->pd3dEffect)
	{
		MessageBox(NULL, L"couldn't load shader", (LPCWSTR)effect->m_Source.GetName().c_str(), 0);
		exit(-1);
	}

	HRESULT hr = effect->pd3dEffect->ValidateTechnique(effect->pd3dEffect->GetTechnique(0));
}
void Renderer9::VSetEffect(RE_Effect * effect)
{
	m_pREffect = effect;
}
RE_Effect* Renderer9::VGetEffect()
{
	return m_pREffect;
}

void Renderer9::VReleaseEffect(RE_Effect * effect)
{
	if(effect->pd3dEffect != NULL)
	{
		effect->pd3dEffect->Release();
		effect->pd3dEffect = NULL;
	}
}
void Renderer9::VCreatePixelShader()
{
}
void Renderer9::VCreateVertexShader()
{
}
void Renderer9::VSetPixelShader()
{
}
void Renderer9::VGetPixelShader() 
{
}
void Renderer9::VSetVertexShader()
{
}
void Renderer9::VGetVertexShader()
{
}

void Renderer9::VClear()
{
	m_pd3d9Device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 0, 0, 255 ), 1.0f, 0 );
}
void Renderer9::VSetColorFill()
{
}
void Renderer9::TakeScreenshot(std::wstring location)
{
}
void Renderer9::VCreateAdditionalSwapChain(){ }
void Renderer9::VCreateCubeTextureSurface(){ }
void Renderer9::VCreateDepthStencilSurface(){ }
void Renderer9::VCreateOffscreenPlainSurface(){ }
void Renderer9::VCreateRenderTarget(){ } 
void Renderer9::VUpdateSurface(){ } // copy portion of one texture to another
void Renderer9::VStretchRectangle(){ }


void Renderer9::VUpdateTexture(RE_Texture * texture)
{
	if(texture->pd3d9Texture != NULL)
	{
		return;
	}
	ResHandlePtr handle = REC()->VGetHandle(texture->m_Source);
	if(handle == ResHandlePtr())
	{
			return;
	}
	D3DXCreateTextureFromFileInMemory(m_pd3d9Device, handle->VGetBuffer(), handle->VGetSize(), &texture->pd3d9Texture);
}

void Renderer9::VReleaseTexture(RE_Texture * texture)
{
	if(texture->pd3d9Texture != NULL)
	{
		texture->pd3d9Texture->Release();
		texture->pd3d9Texture = NULL;
	}
}

void Renderer9::VSetTexture( RE_Texture * texture)
{
	if(texture->pd3d9Texture != NULL)
	{
		m_pTexture = texture;
	}
}

void Renderer9::VSetSamplerState()
{
}

void Renderer9::VSetMaterial(RE_Material * mat)
{
	m_pMaterial = mat;
}
RE_Material* const Renderer9::VGetMaterial()
{
	return m_pMaterial;
}



void Renderer9::VSetDeclaration(D3DVERTEXELEMENT9 * vd)
{
	IDirect3DVertexDeclaration9 * vdp;
	m_pd3d9Device->CreateVertexDeclaration(vd, &vdp);
	m_pd3d9Device->SetVertexDeclaration(vdp);
	vdp->Release();
}
D3DVERTEXELEMENT9* Renderer9::VGetDeclaration()
{
	return NULL;
}